﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExcelFileQueryTool
{
    public static class MessageDispatcherManager
    {
        private enum DispatchSessionState : short
        {
            None,
            Begin,
            End
        }

        private static List<IMessageDispatcher> Dispatchers;
        private static ConcurrentDictionary<string, DispatchSessionState> SessionState = new ConcurrentDictionary<string, DispatchSessionState>();
        private static List<AppMessage> MessageCache = new List<AppMessage>();

        static MessageDispatcherManager()
        {
            Dispatchers = new List<IMessageDispatcher>();
        }

        public static void SessionBegin(string sessionId)
        {
            if (string.IsNullOrEmpty(sessionId))
                throw new ArgumentNullException("sessionId");

            SessionState.AddOrUpdate(sessionId, DispatchSessionState.Begin, (id, val) => { return DispatchSessionState.Begin; });
        }

        public static void SessionEnd(string sessionId)
        {
            if (string.IsNullOrEmpty(sessionId))
                throw new ArgumentNullException("sessionId");

            if (SessionState.TryUpdate(sessionId, DispatchSessionState.End, DispatchSessionState.Begin))
                DispatchSessionMessage(sessionId);
            else
                throw new Exception("结束消息分发Session的操作失败，因为Session[" + sessionId + "]还没开始。");
        }

        public static void AddDispatcher(IMessageDispatcher dispatcher)
        {
            if (dispatcher == null)
                throw new ArgumentNullException("dispatcher");

            lock (typeof(MessageDispatcherManager))
            {
                if (Dispatchers.Any(x => x.Name == dispatcher.Name))
                    return;

                Dispatchers.Add(dispatcher);
            }
        }

        private static void DispatchSessionMessage(string sessionId)
        {
            var msgList = MessageCache.Where(x => x.DispatchSessionId == sessionId).ToList();
            if (msgList.Count == 0)
                return;

            foreach (var msg in msgList)
            {
                MessageCache.Remove(msg);
            }

            DispatchMessageCore(new AppMessage
            {
                DispatchSessionId = sessionId,
                Time = msgList[0].Time,
                Level = msgList[0].Level,
                Title = msgList[0].Title,
                Content = string.Join(Environment.NewLine, msgList.Select(x => x.Content))
            });
        }

        private static void DispatchMessageCore(AppMessage msg)
        {
            foreach (var dispatcher in Dispatchers)
            {
                dispatcher.Dispatch(msg);
            }
        }

        public static void DispatchMessage(AppMessage msg)
        {
            if (msg == null)
                return;

            if (string.IsNullOrEmpty(msg.DispatchSessionId) == false)
            {
                DispatchSessionState sessionState;
                if (SessionState.TryGetValue(msg.DispatchSessionId, out sessionState))
                {
                    if (sessionState == DispatchSessionState.Begin)
                    {
                        lock (typeof(MessageDispatcherManager))
                        {
                            MessageCache.Add(msg);
                        }
                        return; // if cached then terminate the dispatch action
                    }
                }
            }

            DispatchMessageCore(msg);
        }
    }

    public enum AppMessageLevel : short
    {
        Debug = 1,
        Info = 2,
        Warn = 3,
        Error = 4,
        Fatal = 5
    }

    public sealed class AppMessage
    {
        public AppMessageLevel Level { get; set; }

        public string DispatchSessionId { get; set; }

        public DateTime Time { get; set; }

        public string Title { get; set; }

        public string Content { get; set; }
    }

    public interface IMessageDispatcher
    {
        string Name { get; }

        void Dispatch(AppMessage msg);
    }

    public sealed class LogFileMessageDispatcher : IMessageDispatcher
    {
        public AppMessageLevel MinLevel { get; set; }

        public AppMessageLevel MaxLevel { get; set; }

        public LogFileMessageDispatcher(string name)
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException("name");

            this.Name = name;
            MinLevel = AppMessageLevel.Warn;
            MaxLevel = AppMessageLevel.Fatal;
        }

        private static string ConvertAppMsgToString(AppMessage msg)
        {
            return string.Format("[{0}]{1}\t{2}\r\n{3}", msg.Level.ToString()
                                            , msg.Time.ToString("yyyy/M/d H:m:s")
                                            , msg.Title
                                            , msg.Content);
        }

        private bool ShouldBeDispatched(AppMessage msg)
        {
            return msg.Level >= MinLevel && msg.Level <= MaxLevel;
        }

        public void Dispatch(AppMessage msg)
        {
            if (msg == null)
                return;

            if (ShouldBeDispatched(msg))
            {
                SimpleLogger.Log(ConvertAppMsgToString(msg));
            }
        }

        public string Name
        {
            get;
            private set;
        }
    }

    public sealed class PopupNotifierMessageDispatcher : IMessageDispatcher
    {
        public AppMessageLevel MinLevel { get; set; }

        public AppMessageLevel MaxLevel { get; set; }

        // 全局只允许有一个通知窗口，避免重复显示或显示冲突
        private static PopupNotifier Notifier = new PopupNotifier();

        public PopupNotifierMessageDispatcher(string name)
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException("name");

            this.Name = name;
            MinLevel = AppMessageLevel.Info;
            MaxLevel = AppMessageLevel.Info;

            Notifier.Click += (sender, e) =>
            {
                var context = (PopupNotifier)sender;
                context.Hide();
                using (var f = new frmMessageDetail(context.TitleText, context.ContentText))
                {
                    f.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
                    f.ShowDialog();
                }
            };
        }

        private bool ShouldBeDispatched(AppMessage msg)
        {
            return msg.Level >= MinLevel && msg.Level <= MaxLevel;
        }

        public void Dispatch(AppMessage msg)
        {
            if (ShouldBeDispatched(msg))
            {
                var content = msg.Content;

                if (string.IsNullOrEmpty(content) == false && content.Length > 60)
                    content = content.Substring(0, 60) + "...";

                Notifier.TitleText = msg.Title;
                Notifier.ContentText = content;
                Notifier.Popup();
            }
        }

        public string Name
        {
            get;
            private set;
        }
    }

}
